package pathfinding
{
	import flash.display.Shader;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.utils.Timer;
	
	import pathfinding.FindingAction;
	import pathfinding.Grid;
	import pathfinding.Node;
	
	public class PathFinding extends Sprite
	{
		//private var mapContainer:Sprite;
		
		//mapID:map_ywc1_0, endNode ( 36, 15 ), startNode (35,16)
		
		public static const BG_COLOR:uint = 0x000000;
		
		public static const BG_ALPHA:Number = 0.6;
		
		public static const DRAW_INTERVAL:int = 150;
		
		public static const BLOCK_COLOR:uint = 0xff00000;
		
		public static const CLOSED_COLOR:uint = 0xbbbbbb;
		
		public static const OPEN_COLOR:uint = 0xffff00;
		
		public static const VISIT_COLOR:uint = 0x00ff00;
		
		public static const FINDED_COLOR:uint = 0x33ff66;
		
		public static const UNFIND_COLOR:uint = 0xee2200;
		
		public static const POP_OPEN_COLOR:uint = 0xbbbb00;
		
	
		private var _grid:Grid;
		
		private var infos:Array;
		
		private var gridDrawed:Array;
		
		private var nodeCanvas:Array;
		
		private var timer:Timer;
		
		private  var finding:Array;
		
		public function PathFinding()
		{
			super();
		}
		
		private function drawBg():void
		{
			graphics.beginFill( BG_COLOR, BG_ALPHA );
			
			if( this.stage )
			{
				graphics.drawRect( 0, 0, this.stage.stageWidth, this.stage.stageHeight );
			}
			else
			{
				graphics.drawRect( 0, 0, 820, 540 );
			}
			
			graphics.endFill();
			
		}
		
		private function get nodeWidth():Number{
			return nodeGapH - 2;
		}
		
		
		private var _gridWidth:Number = -1;
		private function get nodeGapH():Number{
			
			if( _gridWidth <= 0 )
			{
				_gridWidth = this.width / _grid.numCols;
			}
			
			return _gridWidth;
			
		}
		
		
		private function get nodeHeight():Number{
			return nodeGapV - 2;
		}
		
		private var _gridHeight:Number = -1;
		private function get nodeGapV():Number{
			
			if( _gridHeight <= 0 )
			{
				_gridHeight = this.height / _grid.numRows;
			}
			
			return _gridHeight;
			
		}
		
		private function drawGrid(  ):void
		{
			var grid:Grid = _grid;
			
			var i:int = 0;
			
			var y:Number = 0;
			
			var x:Number = 0;
			
			graphics.beginFill( 0xffffff, 1 );
			
			for( i = 1; i < grid.numRows; i++ )
			{
				x = 0;
				
				y = i * nodeGapV;
				
				graphics.drawRect( x, y, this.width, 2 );
				
			}
			
			for( i = 1; i < grid.numCols; i++ )
			{
				x = i * nodeGapH;
				
				y = 0;
				
				graphics.drawRect( x, y, 2, this.height );
			}
			
			graphics.endFill();
			
			drawStartNode();
			
			drawEndNode();
			
		}
		
		private function drawBlock():void
		{
			var grid:Grid = _grid;
			
			for( var i:int = 0; i < grid.numRows; i++ )
			{
				for( var j:int = 0; j < grid.numCols; j++ )
				{
					var node:Node = grid.getNode( j, i );
					
					if( !node.walkable )
					{
						drawNode( j, i, BLOCK_COLOR, 1, "unWalkable"  );
					}
					
				}
				
			}
		}
		
		private var textFormat:TextFormat = new TextFormat( "宋体", 10, 0xffffff, true, null, null, null, null, "center");
		
		private function drawNode( x:int, y:int, color:uint = 0xffff00, alpha:Number = 1, info:String = "" ):void
		{
			if( gridDrawed && gridDrawed[x] && gridDrawed[x][y] )
			{
				return;
			}
			
			var point:Point = getRelGridXY( x, y );
			
			var t_x:Number = point.x; 
			
			var t_y:Number = point.y;
			
			
			if( !nodeCanvas[x] )
			{
				nodeCanvas[x] = [];
			}
			
			if( !nodeCanvas[x][y] )
			{
				nodeCanvas[x][y] = new Sprite();
				
				this.addChild( nodeCanvas[x][y] );
			}
			
			var sprite:Sprite = nodeCanvas[x][y] as Sprite;
			
			sprite.graphics.clear();
			sprite.graphics.beginFill( color, alpha );
			sprite.graphics.drawRect( 0, 0, nodeWidth, nodeHeight );
			sprite.graphics.endFill();
			sprite.x = t_x;
			sprite.y = t_y;
			
			
			
			if( info && info.length > 0 )
			{
				var txt:TextField = new TextField();
				txt.width = nodeGapH;
				txt.height = nodeGapV;
				txt.setTextFormat( textFormat );
				txt.defaultTextFormat = textFormat;
				txt.text = info;
				txt.x = t_x;
				txt.y = t_y;
				this.addChild( txt );
				
				if( !infos[x] )
				{
					infos[x] = [];
				}
				
				infos[x][y] = txt;
			}
			
			
			if( !gridDrawed[x] )
			{
				gridDrawed[x] = [];
			}
			
			gridDrawed[x][y] = true;
		}
		
		private function clearNode( x:int, y:int ):void
		{
			if(  !gridDrawed[x] ||  !gridDrawed[x][y] )
			{
				return;
			}
			
			if( infos[x] && infos[x][y] )
			{
				var txt:TextField = infos[x][y] as TextField;
				
				this.removeChild( txt );
				
				infos[x][y] = null;
				
			}
			
//			var point:Point = getRelGridXY( x, y );
//			
//			var t_x:Number = point.x; 
//			
//			var t_y:Number = point.y;
			
			gridDrawed[x][y] = false;
			
			if( !nodeCanvas[x] || !nodeCanvas[x][y] )
			{
				return;
			}
			
			var sprite:Sprite = nodeCanvas[x][y] as Sprite;
			
			sprite.graphics.clear();
			
		}
		
		public function getRelGridXY( x:Number, y:Number ):Point
		{
			return new Point(  x * nodeGapH + 1, y * nodeGapV + 1 );
		}
		
		
		public function init( grid:Grid ):void
		{
			this._grid = grid;
			
			finding = [];
			
			gridDrawed =[];
			
			infos = [];
			
			if( nodeCanvas )
			{
				for( var i:int; i < nodeCanvas.length; i++ )
				{
					 if( !nodeCanvas[i] ) continue;
					
					 for( var j:int = 0; j < nodeCanvas[j]; j++ )
					 {
						 if( !nodeCanvas[i][j] ) continue;
						 
						 removeChild( nodeCanvas[i][j] );
						 
					 }
					
				}
			}
			
			nodeCanvas = [];
			
			_gridWidth = -1;
			
			_gridHeight = -1;
			
			graphics.clear();
			
			drawBg();
			
			drawGrid();
			
			drawBlock();
			
		}
		
		public function showFindPath():void
		{
			 timer = new Timer( DRAW_INTERVAL );
			
			 timer.addEventListener( TimerEvent.TIMER, timerHandler );
			 
			 timer.start();
			
		}
		
		public function timerHandler( e:TimerEvent ):void
		{
			if( finding.length > 0 )
			{
				var findAct:FindingAction = finding.shift();
				
				if( findAct.node.x  == _grid.startNode.x && findAct.node.y == _grid.startNode.y && findAct.type != FindingAction.ACT_VISIT )
				{
					clearNode( findAct.node.x, findAct.node.y );
					drawStartNode();
					return;
				}
				
				if( findAct.node.x  == _grid.endNode.x && findAct.node.y == _grid.endNode.y && findAct.type != FindingAction.ACT_VISIT
					&& findAct.type !=  FindingAction.ACT_FIND && findAct.type != FindingAction.ACT_UNFIND )
				{
					clearNode( findAct.node.x, findAct.node.y );
					drawEndNode();
					return;
				}
				
				
				switch( findAct.type )
				{
					case FindingAction.ACT_FIND:
					{
						clearNode( _grid.endNode.x, _grid.endNode.y );
						
						drawNode( _grid.endNode.x, _grid.endNode.y, FINDED_COLOR, 1, "finded!");
						
						drawFinalPath();
					}
					break;
					
					case FindingAction.ACT_UNFIND:
					{
						clearNode( _grid.endNode.x, _grid.endNode.y );
						
						drawNode( _grid.endNode.x, _grid.endNode.y, UNFIND_COLOR, 1, "unFinded~!");
					}
					break;
					
					case FindingAction.ACT_POP_OPEN:
					{
						clearNode( findAct.node.x, findAct.node.y );
						
						drawNode( findAct.node.x, findAct.node.y, POP_OPEN_COLOR, 1, "poped open");
					}
					break;
					
					case FindingAction.ACT_PUSH_OPEN:
					{
						clearNode( findAct.node.x, findAct.node.y );
						
						drawNode( findAct.node.x, findAct.node.y, OPEN_COLOR, 1, "push open");
					}
					break;
					
					case FindingAction.ACT_PUSH_CLOSE:
					{
						clearNode( findAct.node.x, findAct.node.y );
						
						drawNode( findAct.node.x, findAct.node.y, CLOSED_COLOR, 1, "push closed");
					}
					break;
					
					case FindingAction.ACT_VISIT:
					{
						clearNode( findAct.node.x, findAct.node.y );
						
						drawNode( findAct.node.x, findAct.node.y, VISIT_COLOR, 1, "visiting");
					
					}
					break;
					
					case FindingAction.ACT_UNWALKABLE:
					{
						clearNode( findAct.node.x, findAct.node.y );
						
						drawNode( findAct.node.x, findAct.node.y, BLOCK_COLOR, 1, "unWalkable");
					}
					break;
					
					case FindingAction.ACT_NONE:
					{
						clearNode( findAct.node.x, findAct.node.y );
					}
					break;
					
				}
				
			}
			else
			{
				timer.stop();
				
				timer.removeEventListener( TimerEvent.TIMER, timerHandler );
			}
		}
		
		
		
		public function pushNodeOpen( node:Node ):void
		{
			pushAction( node, FindingAction.ACT_PUSH_OPEN );	
		}
		
		public function popNodeOpen( node:Node ):void
		{
			pushAction( node, FindingAction.ACT_POP_OPEN );	
		}
		
		public function pushNodeClose( node:Node ):void
		{
			pushAction( node, FindingAction.ACT_PUSH_CLOSE );	
		}
		
		public function visitNode( node:Node ):void
		{
			pushAction( node, FindingAction.ACT_VISIT );	
		}
		
		public function unWalkable( node:Node  ):void
		{
			pushAction( node, FindingAction.ACT_UNWALKABLE );	
		}
		
		public function pushNone( node:Node ):void
		{
			pushAction( node, FindingAction.ACT_NONE );
		}
		
		public function find():void
		{
			pushAction( _grid.endNode, FindingAction.ACT_FIND );
			
			showFindPath();
		}
		
		
		public function unFind():void
		{
			pushAction( _grid.endNode, FindingAction.ACT_UNFIND  );
			
			showFindPath();
		}
		
		
		
		
		private function pushAction( node:Node, actType:int ):void
		{
			var findAct:FindingAction = new FindingAction( node, actType );
			
			finding.push( findAct );
			
		}
		
		private function drawStartNode():void
		{
			drawNode( _grid.startNode.x, _grid.startNode.y, 0x0000ff, 1, "start"  );
		}
		
		
		private function drawEndNode():void
		{
			if( _grid.endNode.walkable )
			{
				drawNode( _grid.endNode.x, _grid.endNode.y, 0x0000ff, 1, "end"  );
			}
			else
			{
				drawNode( _grid.endNode.x, _grid.endNode.y, 0xff00ee, 1, "end"  );
			}
		}
		
		private function drawFinalPath():void
		{
			var node:Node = this._grid.endNode.parent;
			
			while ( node && node != this._grid.startNode)
			{
				 clearNode( node.x, node.y );
				 
				 drawNode( node.x, node.y, VISIT_COLOR, 1, "*" );
				 
				 node = node.parent;
			}
		}
		
	}
}